package scene.shape.geometry;
import raytracing.HitRecord;
import raytracing.Ray;
import scene.shape.BoundingBox;
import util.Matrix4f;
import util.Vector3f;
import util.Vector4f;

/**
 * A 3D sphere, defined by it's center and it's radius
 * @author glenn
 *
 */
public class Sphere extends Primitive{
	
	public Vector3f c;
	private Vector3f r;
	
	public Sphere(Vector3f c, float r){
		this.c = c;
		this.r = new Vector3f(r,0,0);
	}

	public HitRecord intersects(Ray ray, float mint, float maxt) {
		Matrix4f invTransform = shape.getInverseTransform();
		Ray oray = new Ray(invTransform.transform(ray.getE(),1), invTransform.transform(ray.getS(),1));
		float r = this.r.length();
		Vector3f d = oray.getDirection(),
				eminusc = oray.getE().subtract(c);
		float a = d.dot(d),
			  b = d.scale(2).dot(eminusc),
			  c = eminusc.dot(eminusc) - r * r,
			  disc = (b*b)-(4*a*c);
		if(disc < 0)
			return HitRecord.NOHIT;
		float t1 = (-b - (float)Math.sqrt(disc)) / (2 * a);
		float t2 = (-b + (float)Math.sqrt(disc)) / (2 * a);
		float t = Math.min(t1,t2);
		if(t < mint || t > maxt)
			return HitRecord.NOHIT;
		Vector3f normal = oray.param(t).subtract(this.c).normalize(); //van centrum naar snijpunt (p-c)
		return new HitRecord(true,t,ray, invTransform.transpose().transform(normal,0),this);
	}
	
	public void transform(Matrix4f transformation) {
		c = new Vector3f(transformation.multiply(new Vector4f(c,1)));
		r = new Vector3f(transformation.multiply(new Vector4f(r,0)));
	}

	@Override
	public BoundingBox getBoundingBox() {
		Vector3f radius = new Vector3f(r.x,r.x,r.x);
		return new BoundingBox(shape.getTransform().transform(c.subtract(radius),1), shape.getTransform().transform(c.add(radius),1));
	}

	@Override
	public float getU(HitRecord hr) {
		Vector3f intersectionPoint = getShape().getInverseTransform().transform(hr.getIntersectionPoint(),1);
		float phi = (float)Math.atan2(intersectionPoint.y - c.y, intersectionPoint.x - c.x);
		if(phi < 0)
			phi = phi + (float)(2*Math.PI);
		return (float)(phi / (2 * Math.PI));
	}

	@Override
	public float getV(HitRecord hr) {
		Vector3f intersectionPoint = getShape().getInverseTransform().transform(hr.getIntersectionPoint(),1);
		float theta = (float)Math.acos((intersectionPoint.z - c.z) / r.x);
		return (float)((Math.PI - theta) / Math.PI);
	}

	@Override
	public HitRecord hit(Ray r, float mint, float maxt) {
		return intersects(r, mint, maxt);
	}
	
	
	
}
